#include "freeman.h"
/**
  @brief 计算像素点链码
  @param [in] image          原始图像
  @param [in] auxSubIndex    图像便利辅助矩阵
  @param [in] width          图像宽
  @param [in] height         图像高
  @param [in] current_p      像素点
  @param [out] current_dir   链码值(当前方向)
  @param [out] main_dir      当前的主方向
*/
void chaincode(uchar image[], uint auxSubIndex[], ushort width, ushort height, position &current_p,
               signed char &current_dir, signed char &main_dir)
{
    position temp_p;
    signed char d = current_dir; //沿着源链码方向左右轮回遍历，先用当前方向，考虑用主方向！
    current_dir = -1;
    for(int k=0; k<8; k++)
    {
        if(k%2){ //奇数
            d = (d-k)%8;
            d = d<0 ? d+8 : d;
        }
        else{
            d = (d+k)%8;
            d = d<0 ? d+8 : d;
        }
        //printf("%d,",d);
        temp_p.row = current_p.row + coor[d][0];
        temp_p.col = current_p.col + coor[d][1];
        if(temp_p.row <0 || temp_p.row >=height-1 || temp_p.col <0 || temp_p.col >=width-1)
            continue;
        if(image[auxSubIndex[temp_p.row]+temp_p.col])
        {
            current_dir = d;
            break;
        }
    }
    //printf("\n");
}
/**
  @brief freeman数字直线(该版本特点：对线段角度分类，未加入跳点检测)
  @param [in] image          原始图像
  @param [in] auxSubIndex    图像遍历辅助矩阵
  @param [in] width          图像宽
  @param [in] height         图像高
  @param [out] line_seg_mat  二维结构体数组，储存分组直线段
  @param [out] line_seg_cnt  分组线段计数数组
*/
void  chaincodeDetectLine(uchar image[], uint auxSubIndex[], ushort width, ushort height,
                          line_template line_seg_mat[][segNumLimit/2], int line_seg_cnt[])
{
    position current_p;
    signed char current_dir = -1;

    bool isSegEmptyFlag = true;

    int cell_cnt =0;
    signed char cell_dir1 =-1;
    signed char cell_dir2 =-1;
    int cell_sublth1 =0;
    int cell_sublth2 =0;

    int sub_cnt =0;
    signed char sub_dir1 = -1;
    signed char sub_dir2 = -1;

//    bool breakFlag = false;
    bool isSubFinishedFlag;
    bool isNotBelongCell;
    bool connectFlag = false;
    int count=0;

    line_template line_sub_cell;
    line_template line_cell;
    int last_seg_idx = 0;

    for(int i =height-1; i>=0; i--)
    {
        //for(int j =width-1; j>=0; j--)
        for(int j =0; j<=width-1; j++)
        {
            //attention dir init
            if(image[auxSubIndex[i]+j])
            {
                count =0;
                current_p.col = j;  //x
                current_p.row = i;  //y
                current_dir = -1;
                sub_cnt = 0;
                sub_dir1 = -1;
                sub_dir2 = -1;

                cell_cnt = 0;
                cell_dir1 = -1;
                cell_dir2 = -1;
                cell_sublth1 = 0;
                cell_sublth2 = 0;
                isSegEmptyFlag = true;

                do{
                    chaincode(image, auxSubIndex, width, height, current_p, current_dir, sub_dir1);
                    count++;
                    image[auxSubIndex[current_p.row]+current_p.col] =0;
                    if(current_dir <0 && count<=1){  //跳过孤立点
                        break;
                    }
                    isSubFinishedFlag = updateLineSubCell(line_sub_cell, sub_cnt, sub_dir1, sub_dir2, current_p, current_dir);
                    if(isSubFinishedFlag) //当出现current_dir=-1,isSubFinishedFlag必定true
                    {
                        isNotBelongCell = updateLineCell(line_cell, cell_cnt, cell_dir1, cell_dir2, cell_sublth1, cell_sublth2, line_sub_cell, sub_dir1, sub_dir2);
                        if(isNotBelongCell)
                        {
                            if(connectFlag){
                                updateLineSeg(line_seg_mat, line_seg_cnt, last_seg_idx, isSegEmptyFlag, line_cell, cell_cnt, 120.0);
                                connectFlag = false;
                            }else
                                updateLineSeg(line_seg_mat, line_seg_cnt, last_seg_idx, isSegEmptyFlag, line_cell, cell_cnt, 60.0);
//                            printf("idx: %d, seg_angle:%f\n",last_seg_idx, line_seg_mat[last_seg_idx][line_seg_cnt[last_seg_idx]-1].dir);
//                            fflush(stdout);
                            line_cell.s = line_sub_cell.s;
                            if(line_sub_cell.pixNum >1)
                                line_cell.e = line_sub_cell.e;
                            else
                                line_cell.e = line_sub_cell.s;
                            line_cell.dir = line_sub_cell.dir;
                            line_cell.pixNum = line_sub_cell.pixNum;
                            cell_dir1 = sub_dir1;
                            cell_dir2 = sub_dir2;
                            cell_sublth1 = line_sub_cell.pixNum;
                            cell_cnt++;
                        }
                        sub_cnt = 0;
                        sub_dir1 = -1;
                        sub_dir2 = -1;
                    }
                    if((signed char)current_dir >=0){
                        current_p.row += coor[current_dir][0];
                        current_p.col += coor[current_dir][1];
                    }else{
                        const int jumpPixN = 2;
                        //搜索当前点(中心)附近5Ｘ５的像素点，（2018/8/22/15：34,应该沿用之前的方向信息，这样才能起到抗划痕效果！）
                        //cell_cnt 空的话，对断点处的line_seg线段进行 N 个跳点延长;如果不空,应对line_cell里面的线段进行延长！对下面的代码进行更改!
                        //current_dir =-1;
                        //isSegEmptyFlag = true; //把这句去掉，则不用另起一个线段元，seg_cnt明显减少，但检测结果是正确的

                        //将当前是结束点 且sub_cell属于cell 导致while循环结束sub_cell被吃掉的情况考虑进去
                        if(cell_cnt >0){
                            if(connectFlag){
                                updateLineSeg(line_seg_mat, line_seg_cnt, last_seg_idx, isSegEmptyFlag, line_cell, cell_cnt, 120.0);
                                connectFlag = false;
                            }else
                                updateLineSeg(line_seg_mat, line_seg_cnt, last_seg_idx, isSegEmptyFlag, line_cell, cell_cnt, 60.0);

                        }

                        int idxSub = line_seg_cnt[last_seg_idx]-1;
                        line_template *lp = &line_seg_mat[last_seg_idx][idxSub];
//                        printf("%f,%d\n",lp->dir,lp->pixNum);
//                        fflush(stdout);
                        if(lp->pixNum ==1){
                            position tempp= lp->s;
                            lp = &line_seg_mat[last_seg_idx][idxSub-1];
                            if(lp->pixNum>1 && (abs(lp->e.col-tempp.col)+abs(lp->e.row-tempp.row))<=1){

                            }else
                                lp = NULL;
                        }
                        if(lp)
                        {
                            int diff_X = lp->e.col - lp->s.col;
                            int diff_Y = lp->e.row - lp->s.row;

                            if(abs(diff_X) >= abs(diff_Y)){ //diff_X必大于0
                                if(diff_X >0)
                                    current_p.col += jumpPixN;
                                else
                                    current_p.col -= jumpPixN;
                                current_p.row = lp->s.row + (current_p.col - lp->s.col)/diff_X*diff_Y ;
                            }else{
                                if(diff_Y >0)
                                    current_p.row += jumpPixN;
                                else
                                    current_p.row -= jumpPixN;
                                current_p.col =  lp->s.col + (current_p.row - lp->s.row)/diff_Y*diff_X;
                            }
                            //printf("s(x,y)=(%d,%d), e(x,y)=(%d,%d) ,cp(x,y)=(%d,%d)\n",lp->s.col, lp->s.row, lp->e.col, lp->e.row, current_p.col, current_p.row);
                            //fflush(stdout);
                            if(current_p.row <0 || current_p.row >=height || current_p.row <0 || current_p.row >= width) //说明第seg_cnt-1线段末尾端点已到达图像边缘
                                break;
                            if(image[auxSubIndex[current_p.row]+current_p.col])
                                chaincode(image, auxSubIndex, width, height, current_p, current_dir, sub_dir1);
                            else{//搜索当前点(中心)附近3Ｘ3的像素点; (可考虑5X5)
                                for(int m=current_p.row-2; m <= current_p.row+2; m++)
                                {
                                    for(int n=current_p.col-2; n <= current_p.col+2; n++)
                                    {
                                        if(m<0 || m>=height || n<0 || n>= width)
                                            continue;
                                        if(image[auxSubIndex[m]+n])
                                        {
                                            current_p.col = n;  //x
                                            current_p.row = m;  //y
                                            chaincode(image, auxSubIndex, width, height, current_p, current_dir, sub_dir1);
                                            connectFlag = true;
                                        }
                                        if(current_dir >=0)
                                            break;
                                    }
                                    if(current_dir >=0)
                                        break;
                                }
                            }
                        }
                        if(current_dir<0) //进行上述操作仍然无固定方向，结束整个while()循环;
                            break;
                    }
                }while(1);

            }
        }
    }
}

/**
  @brief 更新线段子元
  @param [out] sub          当前线段子元
  @param [out] subCnt       线段子元像素点计数
  @param subDir1            线段子元方向1
  @param subDir2            线段子元方向2
  @param [in] curP          当前像素点图像位置
  @param [in] curDir        当前像素点链码(方向)
  @retval true              当前点能加入当前线段子元
  @retval false             当前点能加入当前线段子元
*/
bool updateLineSubCell(line_template &sub, int &subCnt, signed char &subDir1, signed char &subDir2,
                       position &curP, signed char &curDir)
{
    bool isFinished = false;
    if(subCnt == 0)
    {
        subCnt++;
        subDir1 = curDir; //subDir1有可能是-1
        sub.s = curP;
        sub.dir = curDir;
        sub.pixNum = subCnt;
        if(curDir%2)   //当前方向为奇数，结束
            isFinished = true;
    }else if(curDir == subDir1)
    {
        subCnt++;
        sub.e = curP;
        sub.pixNum = subCnt;
        isFinished = false;
    }else if(curDir != subDir1)
    {
        subCnt++;
        subDir2 = curDir;
        sub.e = curP;
        sub.pixNum = subCnt;
        isFinished = true;
    }
    return isFinished;
}

/**
  @brief 更新线段元
  @param [out] cell         当前线段元
  @param [out] cellCnt      线段元像素点计数
  @param cellDir1           线段元方向1
  @param cellDir2           线段元方向2
  @param cellLth1           线段元包含的线段子元长度
  @param cellLth2           线段元包含的线段子元长度
  @param [in] sub           当前线段子元
  @param [in] subDir1       当前线段子元方向1
  @param [in] subDir2       当前线段子元方向2
  @retval true              当前线段子元属于当前线段元
  @retval false             当前线段子元不属于当前线段元
*/
bool updateLineCell(line_template &cell, int &cellCnt, signed char &cellDir1, signed char &cellDir2, int &cellLth1,
                    int &cellLth2, line_template &sub, signed char &subDir1, signed char &subDir2)
{
    bool notBelong;
    int dirCnt=0;
    if(cellCnt == 0)
    {
        notBelong = false;
        cellCnt++;
        cell.s = sub.s;
        if(sub.pixNum>1)
            cell.e = sub.e;
        cell.dir = sub.dir;
        cell.pixNum = sub.pixNum;
        cellDir1 = subDir1;
        cellDir2 = subDir2;
        cellLth1 = sub.pixNum;
    }else
    {
        int N=2;
        signed char dir[4]={cellDir1,subDir1,-1,-1};
        signed char doubleDir[2]={-1,-1};
        if(cellDir2 >=0){
            dir[2] = cellDir2;
            N++;
        }
        if(subDir2 >=0){
            if(N==3)
                dir[3] = subDir2;
            else
                dir[2] = subDir2;
            N++;
        }
        for(int i=0; i<N; i++)
        {
            int j=i+1;
            for( ; j<N; j++){
                if(dir[i] == dir[j])
                    break;
            }
            if(j==N && dir[i]>=0){ //2018/8/15/14:24 防止出现cellDir1和subDir1其中有-1
                dirCnt++;
                doubleDir[dirCnt-1] = dir[i];
            }
        }
        if(dirCnt >2)
            notBelong = true;
        else if(dirCnt ==2 && abs(doubleDir[0]-doubleDir[1])!=1 && abs(doubleDir[0]-doubleDir[1])!=7)
            notBelong = true;
        else if(cellLth1 >0 && abs(sub.pixNum-cellLth1)>1)
            notBelong = true;
        else if(cellLth2 >0 && abs(sub.pixNum-cellLth2)>1)
            notBelong = true;
        else
        {
            notBelong = false;//属于   //通过测试
            cellCnt++;
            if(sub.pixNum ==1)
                cell.e = sub.s;
            else
                cell.e = sub.e;
            cell.pixNum += sub.pixNum;

            cellDir1 = doubleDir[0];
            cellDir2 = doubleDir[1];
            if(cellLth1 <=0)
                cellLth1 = sub.pixNum;
            else if(cellLth2 <=0 && sub.pixNum !=cellLth1 )
                cellLth2 = sub.pixNum;
        }
    }
    return notBelong;
}

/**
  @brief 更新线段二维结构体矩阵，更新线段数量数组，更新上一线段下标
  @param seg_mat    分组线段二维数组
  @param seg_cnt    分组线段计数数组
  @param last_idx   上一线段在数组中的下标
  @param emptyFlag  线段是否空标识
  @param cell       线段元
  @param cellCnt    当前线段元中线段子元的个数
  @param alpha      判断当前线段元属于当前线段的角度偏差阈值
*/
void updateLineSeg(line_template seg_mat[][segNumLimit/2], int seg_cnt[],int &last_idx, bool &emptyFlag,
                   line_template &cell, int &cellCnt, float alpha)
{
    //const float alpha = 1.571;
    //const float alpha = 0.7855;
    //const float alpha = 0.3928;
    //const float radian_3 = 0.05236;
    //const float alpha = 45.0;
    //const float alpha = 45.0;
    const float angle_3 = 3;
    int idx;
    position LCE =cell.e, LCS =cell.s;
    float thetaLC =0.0;   //(-90,90]
    int N,M = cell.pixNum;
    if(M ==1){
        int temp = (char)cell.dir%4;
        if(temp ==0)
            thetaLC =0.0;
        else if(temp ==1)
            thetaLC = -45.0;
        else if(temp ==2)
            thetaLC = 90.0;
        else if(temp ==3)
            thetaLC = 45.0;
        LCE = LCS; //线段元退化为一点
    }else{
        if(LCE.col == LCS.col)
            thetaLC = 90.0;
        else
            thetaLC = atan((float)(LCE.row-LCS.row)/(float)(LCE.col-LCS.col))*180/3.141593;
    }
    if(emptyFlag)
    {
        emptyFlag = false;
        idx = (int)ceil(thetaLC/angle_3 +30)-1;
//        printf("%.4f, %d\n",thetaLC, idx);
//        fflush(stdout);
        seg_mat[idx][seg_cnt[idx]].s = LCS;
        seg_mat[idx][seg_cnt[idx]].e = LCE;
        seg_mat[idx][seg_cnt[idx]].dir = thetaLC;
        seg_mat[idx][seg_cnt[idx]].pixNum = M;
        seg_cnt[idx]++;
        last_idx = idx;
    }else{
        position LSS = seg_mat[last_idx][seg_cnt[last_idx]-1].s;//上一线段的起点
        float thetaLS = seg_mat[last_idx][seg_cnt[last_idx]-1].dir, nextThetaLS =0.0;

        N = seg_mat[last_idx][seg_cnt[last_idx]-1].pixNum;

        if(LCE.col == LSS.col)
            nextThetaLS = 90.0;
        else
            nextThetaLS = atan((float)(LCE.row-LSS.row)/(float)(LCE.col-LSS.col))*180/3.141593;

        if(fabs(thetaLS-thetaLC) < (float)(alpha/(M+N)) || (fabs(nextThetaLS-thetaLS) < (float)(alpha/(M+N)) && M<N)) //这里的判断条件很关键
        {
//            printf("%.4f, %d\n",nextThetaLS, idx);
//            fflush(stdout);
            idx = (int)ceil(nextThetaLS/angle_3 +30)-1;
            if(idx != last_idx){
                position p={0,0};
                seg_mat[last_idx][seg_cnt[last_idx]-1].s = p;
                seg_mat[last_idx][seg_cnt[last_idx]-1].e = p;
                seg_mat[last_idx][seg_cnt[last_idx]-1].dir = 0.0;
                seg_mat[last_idx][seg_cnt[last_idx]-1].pixNum = 0;
                seg_cnt[last_idx]--;
                last_idx = idx;
                seg_mat[last_idx][seg_cnt[last_idx]].s = LSS;
                seg_mat[last_idx][seg_cnt[last_idx]].e = LCE;
                seg_mat[last_idx][seg_cnt[last_idx]].dir = nextThetaLS;
                seg_mat[last_idx][seg_cnt[last_idx]].pixNum = M+N;
                seg_cnt[last_idx]++;

            }else{
                seg_mat[last_idx][seg_cnt[last_idx]-1].e = LCE;
                seg_mat[last_idx][seg_cnt[last_idx]-1].dir = nextThetaLS;
                seg_mat[last_idx][seg_cnt[last_idx]-1].pixNum = M+N;
//                printf("seg_angle:%f\n",line_seg_mat[last_idx][seg_cnt[last_idx]-1].dir);
//                fflush(stdout);
            }
        }else                                       //新线段
        {
            last_idx = (int)ceil(thetaLC/angle_3 +30)-1;
//            printf("%.4f, %d\n",nextThetaLS, last_idx);
//            fflush(stdout);
            seg_mat[last_idx][seg_cnt[last_idx]].s = LCS;
            seg_mat[last_idx][seg_cnt[last_idx]].e = LCE;
            seg_mat[last_idx][seg_cnt[last_idx]].dir = thetaLC;
            seg_mat[last_idx][seg_cnt[last_idx]].pixNum = M;
            seg_cnt[last_idx]++;
        }
    }
    cellCnt =0;
}
